#include "tregist.h"
#include "tapi32v.h"

TRegistry::TRegistry( LPCSTR company, LPSTR appName, StrMode mode )
: _openCount( 0 )
, _strMode( mode )
{
	ChangeApp( company, appName );
}

TRegistry::TRegistry( LPCWSTR company, LPCWSTR appName, StrMode mode )
: _openCount( 0 )
, _strMode( mode )
{
	ChangeAppV( company, appName );
}

TRegistry::TRegistry( HKEY topKey, StrMode mode )
: _topKey( topKey )
, _strMode( mode )
, _openCount( 0 )
{

}

TRegistry::~TRegistry()
{
	while( _openCount > 0 )
		CloseKey();
}

bool TRegistry::ChangeApp( LPCSTR company, LPSTR appName )
{
	if( !TAPI32V::IS_WINNT_V )
		return ChangeAppV( company, appName );
	Wstr companyW( company, _strMode );
	Wstr appNameW( appName, _strMode );
	return ChangeAppV( companyW, appNameW );
}

bool TRegistry::ChangeAppV( const void* company, const void* appName )
{
	while( _openCount > 0 )
		CloseKey();

	_topKey = HKEY_CURRENT_USER;
	WCHAR wbuf[100];
	TAPI32V::sprintfV( wbuf, TAPI32V::IS_WINNT_V ? (void*)L"software\\%s" : (void*)"software\\%s", company );
	if( appName && TAPI32V::GetChar( appName, 0 ) )
		TAPI32V::sprintfV( wbuf + TAPI32V::strlenV(wbuf), L"\\%s", appName );
	return CreateKeyV( wbuf );
}

void TRegistry::ChangeTopKey( HKEY topKey )
{
	while( _openCount > 0 )
		CloseKey();
	_topKey = topKey;
}

bool TRegistry::OpenKey( LPCSTR subKey, bool createFlag )
{
	if( !TAPI32V::IS_WINNT_V )
		return OpenKeyV( subKey, createFlag );
	Wstr subKeyW( subKey, _strMode );
	return OpenKeyV( subKeyW, createFlag );
}

bool TRegistry::OpenKeyV( const void* subKey, bool createFlag )
{
	if( _openCount >= MAX_KEYARRAY )
		return false;
	HKEY parentKey = _openCount == 0 ? _topKey : _hKeys[_openCount - 1];
	LONG status = 0;
	if( createFlag )
	{	
		DWORD tmp;
		status = ::TAPI32V::RegCreateKeyExV( parentKey, subKey, 0, NULL, REG_OPTION_NON_VOLATILE,
			KEY_ALL_ACCESS, NULL, &_hKeys[_openCount], &tmp );
	}
	else
	{
		status = ::TAPI32V::RegOpenKeyExV( parentKey, subKey, 0, KEY_ALL_ACCESS, &_hKeys[_openCount] );
		if( status != ERROR_SUCCESS )
			status = ::TAPI32V::RegOpenKeyExV( parentKey, subKey, 0, KEY_READ, &_hKeys[_openCount] );
	}
	if( status != ERROR_SUCCESS )
		return false;
	++_openCount;
	return true;
}

bool TRegistry::CloseKey( void )
{
	if( _openCount <= 0 )
		return false;
	::RegCloseKey( _hKeys[--_openCount ] );
	return true;
}

bool TRegistry::EnumKey( DWORD cnt, LPSTR buf, int size )
{
	if ( !TAPI32V::IS_WINNT_V ) 
		return	EnumKeyV( cnt, buf, size );
	Wstr buf_w(size);
	if ( !EnumKeyV( cnt, buf_w.Buf(), size ) ) 
		return false;
	WtoS(buf_w, buf, size, _strMode);
	return	true;
}

bool TRegistry::EnumKeyV( DWORD cnt,void* buf, int size )
{
	return	TAPI32V::RegEnumKeyExV( _hKeys[_openCount-1], cnt, buf, (DWORD *)&size, 0, 0, 0, 0 )
		== ERROR_SUCCESS;
}

bool TRegistry::EnumValue( DWORD count, LPSTR buf, int size, DWORD* type )
{
	if ( !TAPI32V::IS_WINNT_V ) 
		return EnumValueV( count, buf, size, type );

	Wstr buf_w(size);
	if (!EnumValueV( count, buf_w.Buf(), size, type ) )
		return false;
	WtoS(buf_w, buf, size, _strMode);
	return	true;
}

bool TRegistry::EnumValueV( DWORD count, void* buf, int size, DWORD* type )
{
	return	TAPI32V::RegEnumValueV( _hKeys[_openCount-1], count, buf, (DWORD *)&size, 0, type, 0, 0 )
		== ERROR_SUCCESS;
}

bool TRegistry::GetInt( LPCSTR key, int* val )
{
	return GetLong( key, (long*)val );
}

bool TRegistry::GetIntV( const void* key, int* val )
{
	return GetLongV( key, (long*)val );
}

bool TRegistry::SetInt( LPCSTR key, int val )
{
	return SetLong( key, val );
}

bool TRegistry::SetIntV( const void* key, int val )
{
	return SetLongV( key, val );
}

bool TRegistry::GetLong( LPCSTR key, long* val )
{
	if( !TAPI32V::IS_WINNT_V )
		return GetLongV( key, val );
	Wstr keyW( key, _strMode );
	return GetLongV( keyW, val );
}

bool TRegistry::GetLongV( const void* key, long* val )
{
	DWORD type = REG_DWORD;
	DWORD size = sizeof( long );
	if( TAPI32V::RegQueryValueExV( _hKeys[_openCount - 1], key, 0, &type, (BYTE*)val, &size ) == ERROR_SUCCESS )
		return true;

	WCHAR wbuf[100] = { 0 };
	long byteSize = sizeof(wbuf);
	if( TAPI32V::RegQueryValueV( _hKeys[_openCount-1], key, wbuf, &byteSize ) != ERROR_SUCCESS )
		return false;
	*val = TAPI32V::strtolV( wbuf, 0, 10 );
	return true;
}

bool TRegistry::SetLong( LPCSTR key, long val )
{
	if( !TAPI32V::IS_WINNT_V )
		return SetLongV( key, val );
	Wstr keyW( key, _strMode );
	return SetLongV( keyW, val );
}

bool TRegistry::SetLongV( const void* key, long val )
{
	return TAPI32V::RegSetValueExV( _hKeys[_openCount-1], key, 0, REG_DWORD, (const BYTE*)&val,
		sizeof(val) ) == ERROR_SUCCESS;
}

bool TRegistry::GetStr( LPCSTR key, LPSTR str, int byteSize )
{
	if( !TAPI32V::IS_WINNT_V )
		return GetStrV( key, str, byteSize );
	Wstr keyW( key, _strMode );
	Wstr strW( byteSize );
	if( !GetStrV( keyW, strW.Buf(), byteSize ) )
		return false;
	WtoS( strW, str, byteSize, _strMode );
	return true;
}

bool TRegistry::GetStrA( LPCSTR key, LPSTR str, int byteSize )
{
	DWORD type = REG_SZ;
	if( RegQueryValueExA( _hKeys[_openCount-1], key, 0, &type, (BYTE*)str, (DWORD*)&byteSize ) != ERROR_SUCCESS 
		&& RegQueryValueA( _hKeys[_openCount-1], key, str, (LPLONG)&byteSize ) != ERROR_SUCCESS )
		return false;
	return true;
}

bool TRegistry::GetStrV( const void* key, void* str, int byteSize )
{
	DWORD type = REG_SZ;
	if( TAPI32V::RegQueryValueExV( _hKeys[_openCount-1], key, 0, &type, (BYTE*)str, (DWORD*)&byteSize ) != ERROR_SUCCESS
		&& TAPI32V::RegQueryValueV( _hKeys[_openCount-1], key, str, (LPLONG)&byteSize ) != ERROR_SUCCESS )
		return false;
	return true;
}

bool TRegistry::SetStr( LPCSTR key, LPCSTR str )
{
	if( !TAPI32V::IS_WINNT_V )
		return SetStrV( key, str );
	Wstr keyW( key, _strMode );
	Wstr strW( str, _strMode );
	return SetStrV( keyW, strW );
}

bool TRegistry::SetStrA( LPCSTR key, LPCSTR str )
{
	return RegSetValueExA( _hKeys[_openCount-1], key, 0, REG_SZ, (const BYTE*)str, (DWORD)strlen(str) + 1 ) == ERROR_SUCCESS;
}

bool TRegistry::SetStrV( const void* key, const void* str )
{
	return	TAPI32V::RegSetValueExV( _hKeys[_openCount-1], key, 0, REG_SZ, (const BYTE*)str,
		(TAPI32V::strlenV(str) +1) * TAPI32V::CHAR_LEN_V ) == ERROR_SUCCESS;
}

bool TRegistry::GetByte( LPCSTR key, BYTE* data, int* size )
{
	if (!TAPI32V::IS_WINNT_V) 
		return	GetByteV( key, data, size );
	Wstr	subKey_w( key, _strMode );
	return	GetByteV( subKey_w, data, size );
}

bool TRegistry::GetByteV( const void* key, BYTE* data, int* size )
{
	DWORD	type = REG_BINARY;
	return	TAPI32V::RegQueryValueExV( _hKeys[_openCount-1], key, 0, &type,
		(BYTE *)data, (LPDWORD)size ) == ERROR_SUCCESS;
}

bool TRegistry::SetByte( LPCSTR key, BYTE* data, int size )
{
	if (!TAPI32V::IS_WINNT_V) 
		return	SetByteV( key, data, size );

	Wstr	subKey_w( key, _strMode );
	return	SetByteV( subKey_w, data, size );
}

bool TRegistry::SetByteV( const void* key, BYTE* data, int size )
{
	return	TAPI32V::RegSetValueExV( _hKeys[_openCount-1], key, 0, REG_BINARY, data, size ) == ERROR_SUCCESS;
}

bool TRegistry::DeleteKey( LPCSTR key )
{
	if( !TAPI32V::IS_WINNT_V )
		return DeleteKeyV( key );
	Wstr subKeyW( key, _strMode );
	return DeleteKeyV( subKeyW );
}

bool TRegistry::DeleteKeyV( const void* key )
{
	return	TAPI32V::RegDeleteKeyV( _hKeys[_openCount-1], key ) == ERROR_SUCCESS;
}

bool TRegistry::DeleteValue( LPCSTR str )
{
	if (!TAPI32V::IS_WINNT_V) 
		return DeleteValueV( str );

	Wstr	subValue_w( str, _strMode );
	return	DeleteValueV(subValue_w);
}

bool TRegistry::DeleteValueV( const void* str )
{
	return	TAPI32V::RegDeleteValueV( _hKeys[_openCount-1], str ) == ERROR_SUCCESS;
}

bool TRegistry::DeleteChildTree( LPCSTR subKey )
{
	if( TAPI32V::IS_WINNT_V )
		return DeleteChildTreeV( subKey );
	Wstr subKeyW( subKey );
	return DeleteChildTreeV( subKeyW );
}

bool TRegistry::DeleteChildTreeV( const void* subKey )
{
	WCHAR wbuf[256] = {0};
	if( subKey && !OpenKeyV( subKey ) )
		return false;

	bool ret = true;
	while( EnumKeyV( 0, wbuf, sizeof(wbuf)/TAPI32V::CHAR_LEN_V ) )
	{
		ret = DeleteChildTreeV( wbuf );
		if( !ret) break;		
	}
	if( subKey != NULL )
	{
		CloseKey();
		ret = DeleteKeyV( subKey ) ? ret : false;
	}
	else
	{
		while( EnumValueV( 0, wbuf, sizeof(wbuf)/TAPI32V::CHAR_LEN_V ) )
		{
			if( !DeleteValueV( wbuf ) )
			{
				ret = false;
				break;
			}
		}
	}
	return ret;
}

void TRegistry::SetStrMode( StrMode mode )
{
	_strMode = mode;
}